package com.ssfeng.proxy2;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;

/**
 * Created by liyuhang on 2020-09-10.
 */
public class MyProxy2 {
    public static final String ln = "\r\n";

    public static Object newProxyInstance(Object object, Class<?>[] interfaces, MyInvocationHandler h) {
        try {
            //1 java源码
            String src = src(interfaces);

            // 编译
            Map<String, byte[]> classMap = compile("$Proxy0.java", src);

            // 类加载器
            MemoryClassLoader memoryClassLoader = new MemoryClassLoader(classMap);

            //2. 源码输出到java文件中，并保存到本地
            System.out.println();
            Class proxyClass = memoryClassLoader.findClass(object.getClass().getPackage().getName() + ".$Proxy0");

            //5、获取构造方法
            Constructor constructor = proxyClass.getConstructor(MyInvocationHandler.class);

            //构造代理对象
            return constructor.newInstance(h);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 结构固定
     * @param interfaces
     * @return
     */
    private static String src(Class<?>[] interfaces) {
        // 找包名
        String packageName = interfaces[0].getPackage().getName();

        StringBuffer sb = new StringBuffer();
        sb.append("package " + packageName + ";" + ln);
        sb.append("import java.lang.reflect.Method;" + ln);
        sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
        sb.append("private MyInvocationHandler h;"+ln);
        sb.append("public $Proxy0(MyInvocationHandler h) { " + ln);
        sb.append("this.h = h;"+ln);
        sb.append("}" + ln);
        for (Method m : interfaces[0].getMethods()) {
            sb.append("public " + m.getReturnType().getName() + " "
                    + m.getName() + "() {" + ln);
            sb.append("try{" + ln);
            sb.append("Method m = " + interfaces[0].getName()
                    + ".class.getMethod(\"" + m.getName()
                    + "\",new Class[]{});" + ln);
            sb.append("this.h.invoke(this,m,null);" + ln);
            sb.append("}catch(Throwable e){" + ln);
            sb.append("e.printStackTrace();" + ln);
            sb.append("}"+ln);
            sb.append("}"+ln);
        }
        sb.append("}" + ln);
        System.out.println(sb.toString());
        return sb.toString();
    }


    /**
     * 直接内存编译.java，编译完成后的字节码放入map中
     * @param javaName
     * @param javaSrc
     * @return
     */
    public static Map<String, byte[]> compile(String javaName, String javaSrc) {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager stdManager = compiler.getStandardFileManager(null, null, null);

        try (MemoryJavaFileManager manager = new MemoryJavaFileManager(stdManager)) {
            JavaFileObject javaFileObject = manager.makeStringSource(javaName, javaSrc);
            JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, Arrays.asList(javaFileObject));
            if (task.call())
                return manager.getClassBytes();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
